package ocp.context;

import ocp.context.adapter.*;
import ocp.context.xml.*;
import java.util.Enumeration;
import java.util.Vector;
import org.xml.sax.*;
import org.xml.sax.helpers.*;
import ocp.context.source.ContextSource;

/**
 * This class represents the simplest form of context. Every simple context is
 * composed of a unique contextual situation, formed by a bunch of contextual
 * informations.
 * @author Ignacio Nieto Carvajal
 */
public class SimpleContext extends Context implements java.io.Serializable {

    /** The contextual information objects that defines this context */
    protected ContextualInformationList data = new ContextualInformationList();
    /** contextual factory used to build the adapters needed to get the low level information */
    protected Vector contextSources = null;
    /** number of contextual sources working */
    protected int csWorking = 0;

    /** The main constructor of the class.
     * @param name this context name.
     * @param id the unique id of this context.
     */
    public SimpleContext(String name, String id) {
        super(name, id);
        contextSources = new Vector();
        try {
            updateContextualInformation();
        } catch (ContextualInformationAbsentException e) {
        }
    }

    /** adds a contextual source to this simple context */
    public void addContextSource(ContextSource cs) {
        contextSources.addElement(cs);
        cs.setSourceContext(this);
    }

    /** removes a contextual source from this simple context */
    public void removeContextSource(ContextSource cs) {
        contextSources.removeElement(cs);
    }

    /**
     * Clears all contextual information from this context. After this method is
     * executed, the context is semantically empty.
     */
    public void clearContext() {
        contextSources = new Vector();
        data = new ContextualInformationList();
    }

    /** Adds a contextual information to this context. */
    public void addContextualInformation(ContextualInformation ci) {
        data.addContextualInformation(ci);
    }

    /** adds a contextual information list to this context */
    public void addContextualInformationList(ContextualInformationList cil) {
        data.addContextualInformationList(cil);
    }

    /** Removes a contextual information from this context.
     * @return true if contextual information was found and deleted, false otherwise
     */
    public boolean removeContextualInformation(ContextualInformation ci) {
        return data.removeContextualInformation(ci);
    }

    /** removes a contextual information list from this context.
     * @return an integer with the contextual informations cleaned from the list.
     */
    public int removeContextualInformationList(ContextualInformationList cil) {
        updateTime();
        return data.removeContextualInformationList(cil);
    }

    /**
     * Removes a contextual information based on its name from this context.
     * @param contName name of the contextual information to delete.
     * @return true if contextual information was found and deleted, false otherwise
     */
    public boolean removeContextualInformation(String contName) {
        return data.removeContextualInformation(contName);
    }

    /**
     * Modifies certain contextual information from this context. A valid
     * contextual information object must be given as argument.
     * @param ci ContextualInformation Object to modify.
     * @return true if modification was successful. False if the ContextualInformation ci is not on this context or if the class of newValue does not match with that of ci.
     */
    public boolean modifyContextualInformation(ContextualInformation ci) {
        return data.modifyContextualInformation(ci);
    }

    /**
     * Returns all the contextual information elements from this simplex
     * context. This method is very generic and should not be used if a more
     * specific method is defined.
     */
    public ContextualInformationList getContextualInformation() throws ContextualInformationAbsentException {
        if ((data == null) || (data.isEmpty())) {
            updateContextualInformation();
        }
        return data;
    }

    /**
     * This method updates the contextual information by communicating with
     * the concrete adaptor of the context, that extracts this information
     * from the device, the middleware, the user or any other source.
     * The method from this class calls the generic method
     * updateContextualInformation from the adapter, but this is generally a
     * little specific method. Subclasses of SimpleContext, StaticContext or
     * SituationalContext are encouraged to redefine this method to preciselly
     * update their contextual information.
     */
    public void updateContextualInformation() throws ContextualInformationAbsentException {
        csWorking = 0;

        if (contextSources == null) {
            throw new ContextualInformationAbsentException("Failed to get context. No contextual sources found");
        }
        data = new ContextualInformationList();
        for (int i = 0; i < contextSources.size(); i++) {
            ContextSource cs = (ContextSource) contextSources.elementAt(i);
            if (cs != null) {
                try {
                    data.addContextualInformation(cs.getContextualinformation());
                    csWorking++;
                } catch (IncompatibleContextualObjectException icoe) {
                    System.err.println("Contextual source " + cs.getName() + " failed.");
                } catch (ContextualInformationAbsentException ciae) {
                    System.err.println("Contextual source " + cs.getName() + " not found.");
                }
            }
        }
        if (csWorking == 0) {
            throw new ContextualInformationAbsentException("Failed to get context. Contextual Adapters not working");
        }
    }

    /** returns the number of contextual sources working in the last access
     * to the contextual information.
     */
    public int contextualSourcesWorking() {
        return csWorking;
    }

    /**
     * Returns a XML-formatted string with all the contextual information from
     * this context. The format is:
     * <br><code>
     * &lt;ContextClass name="name of the context"&gt;
     * 		&lt;ContextualInformation1Name description="..." type="..."&gt;
     * 			value
     * 		&lt;/ContextualInformation1Name&gt;
     * 		&lt;ContextualInformation2Name description="..." type="..."&gt;
     * 			value
     * 		&lt;/ContextualInformation2Name&gt;
     * 		&lt;ContextualInformation3Name description="..." type="..."&gt;
     * 			value
     * 		&lt;/ContextualInformation3Name&gt;
     *
     *		...
     *
     * 		&lt;ContextualInformationNName description="..." type="..."&gt;
     * 			value
     * 		&lt;/ContextualInformationNName&gt;
     * &lt;/ContextClass&gt;
     *
     * </code><br>
     * Subclasses of SimpleContext can override this method to generate a more
     * explanatory XML string. This XML string can be easily saved in a XML
     * file by the method storeAsXML(this.toXML()).
     */
    public String toXML() {
        Enumeration e;
        String xml = "<" + this.getClass().getSimpleName() + " name=\"" + name + "\" id=\"" + id + "\" dateTime=\"" + getTime() + "\" class=\"" + this.getClass().getName() + "\">\n";

        for (e = data.getContextualInformation(); e.hasMoreElements();) {
            ContextualInformation ci = (ContextualInformation) e.nextElement();
            if (ci != null) {
                xml += "\t" + ci.toXML() + "\n";
            }
        }
        xml += "\t</" + this.getClass().getSimpleName() + ">\n";

        return xml;
    }

    public boolean loadFromXML(String xmlFile) {
        XMLContextReader xmlcr = new XMLSimpleContextReader(xmlFile);
        if (!xmlcr.isRunning()) {
            try {
                data = (ContextualInformationList) xmlcr.read();
            } catch (IncompatibleContextualObjectException icoe) {
                return false;
            }
        }
        return true;
    }

    public synchronized String extractHierarchyAsString() {
        String hierarchy = "" + this.name + "->";
        for (Enumeration<ContextualInformation> e = data.getContextualInformation(); e.hasMoreElements();) {
            ContextualInformation ci = e.nextElement();
            if (ci != null) {
                if (ci.getContextClass() != null) {
                    hierarchy += ci.getName() + "(" + ci.getContextClass().getName() + "),";
                } else {
                    hierarchy += ci.getName() + ",";
                }
            }
        }
        return hierarchy;
    }
}